"use strict"

module.exports = function(app, settings){
	var log4js 		= require('log4js')
		, _					= require('lodash')
		, bcrypt 		= require('bcrypt-as-promised')
		, cookiee   = require("cookie-encryption")
		, csrf 			= require('csurf') // ref: https://github.com/expressjs/csurf
		, debug 		= require('debug')('identity')
		, jwt 			= require('jsonwebtoken')
		, randomstring = require("randomstring")
		, url 			= require('url')
		, urlencode = require('urlencode')
		, express 	= require('express')
		, fs				= require('fs')
		, util 			= require('util')
		, webRequest  = require('request')
    , template    = require('es6-template-strings')
		, QywxAccessTokenClient = require("./QywxAccessTokenClient");

	var models 		= app.locals.models;

	var logger 								= log4js.getLogger('identity')
		, csrfProtection 				= csrf({ cookie: true })
		, router 								= express.Router()
		, Client 								= models.Client
		, User 									= models.User;


	router.route("/qywx_login")
		.all(function(req, res, next){
			return next();
		})
		.get(function(req, res, next){
			//return login page for this Domain
			var cb 				= req.query.cb;	//cb url after get the token
			var clientId  = req.query.client_id;
			var domain 		= res.locals.domain.name;

			var redirectUri = url.format({
					protocol: req.get('X-Forwarded-Proto') || req.protocol,
					host: req.get('host'),
					pathname: "/identity/oauth2/token",
					query: {
						cb: cb,
						client_id: clientId,
						authub_domain: domain,
						authub_account: res.locals.account
					}
			});

			var qywxAuthUrl = "https://qy.weixin.qq.com/cgi-bin/loginpage?corp_id=wx42c1a7f89c4a258d"
		                + "&redirect_uri="
		                + urlencode(redirectUri)			//56f6bff4caa511fd56c94d88
		                + "&state=1234567890&usertype=admin";

			return res.render("qywxLogin", { authUrl: qywxAuthUrl });
		});

	router.route('/auth')
		.all(function(req, res, next){
			next();
		})
		.get(csrfProtection, function(req, res, next){
			//parameters for GET requests:
			// @param response_type=code
			// @param client_id=xxxxxx
			// @param state=xxxxxxxxx
			// @param callback=
			// @param scope=

			var clientId = req.query.client_id;
			var callback = req.query.callback;
			var responseType = req.query.response_type;
			var state = req.query.state;
			var scope = req.query.scope;

			switch(responseType){
				case 'code':
					//oAuth2 authorization code flow
					if(!callback || !clientId){
						var err = new Error("Missing parameters");
						throw err;
					}

					//check if client_id is valid
					Client.find({ clientId: clientId })
						.then(function(client){
							if(!client) throw err;

							//TODO: check if callback is valid in client's setting
							//callback is a field in Client

							//render the authorization grant assertion page
							return res.render('auth'
										, {
											csrfToken: req.csrfToken()
										}
									);

						});
					break;

				default:
					var err = new Error('Invalid authorization flow');
					throw err;
			}
		})
		.post(csrfProtection, function(req, res, next){
			var responseType = req.query.response_type;

			if(responseType === 'code'){
				var callback = req.query.callback;
				var clientId = req.query.client_id;
				var state = req.query.state;


				return AuthCode.generateAuthCode(clientId,
					res.locals.user._id,
					function(err, authCode){
						//compose callback url
						var cbUrl = url.parse(callback, true);
						cbUrl.query.state = state;
						cbUrl.query.code = authCode.authCode;

						return res.redirect(url.format(cbUrl));
				});

			}

			next();
		});

	//OAuth token endpoint - return JWT token to client
	router.route('/token')
		.all(function(req, res, next){
			next();
		})
		.get(function(req, res, next){
			/**
			 * http://domain.xxx.net/oauth2/token?grant_type=qywx_admin&auth_code=xxxx&expires_in=600
			 * If the request is QYWX Admin request, we will use accessToken and
			 * the authCode to exchange identity information
			 * Wechat QYWX API will redirect to this page with parameters
			 */

			debug("token callback from qywx : ", req.originalUrl);

			var authCode 	= req.query.auth_code;
			var expiresIn = req.query.expires_in;
			var cb 				= req.query.cb;
			var clientId  = req.query.client_id;

			var domain 			= res.locals.domain;
			var corpId 			= domain.qywx.corpId;
			var corpSecret 	= domain.qywx.corpSecret;

			var isValidClient = false;
			for( var i=0; i < domain.clients.length; i++){
				var client = domain.clients[i];
				debug("client in domain: ", client);
				if(client == clientId)	{
					isValidClient = true;
					break;
				}
			}
			if(!isValidClient){
				return res.status(401).json({
					success: false,
					errCode: 401,
					errMsg: "invalid_client"
				});
			}
			var client = QywxAccessTokenClient({
				corpId: corpId,
				corpSecret: corpSecret,
				models: models
			});

			client.get(function(err, result){
				if(err){
					debug(err);
					return res.status(500).json({
						success: false,
						errCode: 500,
						errMsg: 'failed_to_retrieve_access_token',
						internalError: err
					});
				}

				//Get userinfor
				debug("access token result: ", result);

				var GET_LOGIN_INFO_URL = "https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info?access_token=${accessToken}";

				var getLoginInfoUrl = template(GET_LOGIN_INFO_URL, { accessToken: result.accessToken});

				webRequest.post({
					url: getLoginInfoUrl
					,headers: {'Content-Type': 'application/json'}
					,body: JSON.stringify({
	              "auth_code": authCode
	        })
				}, function(error, response, body){
						if(error){
							logger.error(error);
							return res.status(500).json({
								success: false,
								errCode: 500,
								errMsg: 'failed_to_retrieve_login_info',
								internalError: err
							});
						}

						debug("get login info: ", body);

						try{
							var data = JSON.parse(body);

							if(data.errcode){
								return res.status(500).json({
									success: false,
									errCode: data.errcode,
									errMsg: data.errmsg
								});
							}

							//Generate JWT token
							var claims = {
							  sub: (data.usertype == 1 || data.usertype == 3) ? data.user_info.email : data.user_info.userid,
							  iss: req.get('host'),
								detail: data
							};

	        		var accessToken = jwt.sign(claims, domain.jwt.secret
														, {
																algorithms: ['RS256', 'HS256']
																,expiresIn: domain.jwt.expiresIn
												 		}
	        						);
							if(cb){
								var callback = url.parse(cb);

								var query = _.assign({ access_token: accessToken }, callback.query );

								var gotoUrl = url.format({
									protocol: callback.protocol
									,host: callback.host
									,query : query
								});

								return res.redirect(gotoUrl);

							}
						}catch(error){
							return res.status(500).json({
								success: false,
								errCode: 500,
								errMsg: error.message,
								internalError: error
							});
						}
				});
			});
		})
		.post(function(req, res, next){
			//THe POST request put all parameters by x-www-form-urlencoded

			switch(req.body.grant_type){
				case "code":
					var authCode 		= req.body.code
						,clientId 		= req.body.client_id
						,clientSecret = req.body.client_secret;

					//validate code and client
					AuthCode.validateAuthCode(authCode,
						clientId, clientSecret, function(err, code, reason){

						if(err) throw err;

						if(!code){
							return res.json({
				        		success: false,
				        		error: reason,
				        		message: 'Failed to get token'
				        	});
						}


						//Generate JWT token
						var claims = {
						  sub: code.User.username,
						  iss: req.get('host'),
						  permissions: ['all','upload-photos']
						};

        		var accessToken = jwt.sign(claims, jwtPublicKey
													, {
															algorithms: settings.jwt.algorithms
															, expiresIn: settings.jwt.expiresIn
											 		}
        						);

						jwtCookie.write(req, accessToken);

        		return res.json({
        			success: true
							, access_token: accessToken
        		});
					});

					break;
				case "password":
					//TODO: validate the request is from a trusted client's domain
					var username = req.body.username
						,password = req.body.password
						,clientId = req.body.client_id;


					//validate username and password
					User.getAuthenticated(username, password, function(err, user, reason){
			        	if(err) throw err;

			        	if(user !== null){ //success
			        		//Generate JWT token
									var claims = {
									  sub: user.username,
									  iss: req.get('host'),
									  permissions: ['all','upload-photos'] //TODO: get permissions from user table
									};
									var accessToken = jwt.sign(claims, settings.jwt.secret
																, {
																		algorithms: [settings.jwt.algorithms]
																		, expiresIn: settings.jwt.expiresIn
																}
													);
									//jwtCookie.write(req, "accessToken", "stk_dev"); /// failed because of ERROR !!!!

									res.cookie(settings.jwt.cookie.cookie, accessToken, { signed: true });

									return res.json({
										success: true
										, access_token: accessToken
									});
			        	}else{
			        		return res.json({
				        		success: false,
				        		message: 'AuthFailure'
				        	});
			        	}
			        });
					break;
				default:
					return res.json({
						success: false,
						message: "Not supported grant type"
					});
			}
		});

	app.use('/oauth2', router);
}
